<?php
/*
  Multiverse db saves all databases according to universe prefix.
  with universe "universe", table $db->npc is "universe_npc", but treated as
  without the prefix $db->npc
  Usage:
  
  $db = new multiverse_db(
    $config->mysql_host, 
    $config->mysql_username, 
    $config->mysql_password,
    $config->mysql_database);
    
  $db->npc_table->select(array("id" => "1")); 
    // SELECT * FROM universe_npc_table WHERE id = '1'

  $db->people->update(array("name" => "newname"), array("id" => "1")); 
    // UPDATE universe_people SET name = 'newname' WHERE id = '1'

  $db->npc_table->select_all(array("id" => "1")); 
    // Same as select, but returns an array of results
*/

class multiverse_db {
  function __construct(
      $host     = "localhost", 
      $username = "root", 
      $password = "", 
      $database = "mindspace", 
      $universe = "universe") {
    $this->connection = mysql_connect($host, $username, $password);
    mysql_select_db($database, $this->connection);
    $this->database = $database;
    $this->universe = $universe;

    // manually set the connection to use utf8 for äö
    mysql_set_charset('utf8',$this->connection);
    $this->boot();
 }
 
 function boot() {
  	$result = $this->query("SHOW TABLES FROM ".$this->database);
   	while($table = mysql_fetch_assoc($result)) {
   	  $table = $table["Tables_in_".$this->database];
   	  // check for universe_ prefix to enable parallel universes
   	  if (substr($table, 0, strlen($this->universe)+1) == $this->universe."_") {
   	    $table = substr($table, strlen($this->universe)+1);
   	    $this->$table = new table($this, $table, $this->universe);
   	  }
    }
  }
  
  function drop_table($table) {
    if (!$this->valid_table($table)) {
      throw new Exception("Invalid table name");
    }
    $sql = "DROP TABLE ".$this->universe."_".$table;
    $this->query($sql);
  }
  
  function valid_table($table) {
    $forbidden_table_names = array(
      "valid_table", "create_table", "query", "boot", "__construct"
    );
    if (!is_string($table) // check valid string
        || strlen($table) < 1
        || preg_match("/[^-a-z0-9_]/i", $table) // check invalid chars
        || in_array($table, $forbidden_table_names) ) {
      return false;
    }
    return true;
  }
  
  function create_table($table, $arr) {
    // check for illegal chars in table
    if (!$this->valid_table($table)) {
      throw new Exception("Invalid table name");
    }
	  if (!is_array($values)) {
	    throw new Exception("Create needs to be in array form");
	  }
	  $column = implode(",\n", $arr);
    $column = str_replace("\"", "'", $column);
    $column = str_replace(";", "", $column);
    
    $sql = "CREATE TABLE ".$this->universe."_".$table;
    $this->query($sql);
  }

  function query($query) {
    //echo "<font size=-4 color=\"red\">".$query."</font><br>";
    $result = mysql_query($query, $this->connection);
    if (!$result) {
        throw new Exception("Mysql error:  " .
          mysql_errno($this->connection) . ": " . mysql_error($this->connection) . 
          "\n\n\n query: " . $query);
    }
    return $result;
  }
}


class table {
	function __construct($database, $table, $universe){
		$this->table = $table;
		$this->database = $database;
		$this->universe = $universe;
	}
	
	public function select($where = 1, $what = "*"){
		$where = $this->stringify($where);
		if ($what != "*") {
	    if (!is_array($what)) {
	      throw new Exception("Shown tables need to be in array form");
	    }
		  $what = mysql_real_escape_string(implode(", ", $what));
		}

		$query = "SELECT $what FROM ".$this->universe."_".$this->table." WHERE $where";
		//echo $query;
		return mysql_fetch_assoc($this->database->query($query));
	}

	public function select_all($where = 1, $what = "*") {
		$where = $this->stringify($where);
		if ($what != "*") {
	    if (!is_array($what)) {
	      throw new Exception("Shown tables need to be in array form");
	    }
		  $what = mysql_real_escape_string(implode(", ", $what));
		}
		
		$query = "SELECT $what FROM ".$this->universe."_".$this->table." WHERE $where";
		$result = $this->database->query($query);

		while($assoc = mysql_fetch_assoc($result)) {
			$return[] = $assoc;
		}
		return isset($return) ? $return : null;
	}

	public function insert($values) {
	  if (!is_array($values)) {
	    throw new Exception("Insert needs to be in array form");
	  }
		$cols = "";
		$vals = "";
		foreach ($values as $key => $value) {
			$value = mysql_real_escape_string($value);
			$key = mysql_real_escape_string($key);
			$cols .= "$key,";
			$vals .= "'$value',";
		}
		$cols = substr($cols, 0, - 1);
		$vals = substr($vals, 0, - 1);

		$query = "INSERT INTO ".$this->universe."_".$this->table." ($cols) VALUES ($vals)";
		return $this->database->query($query);
	}


	public function update($how, $where = 1){
		if (!isset($where)) {
			$where = 1;
		}
		$how = $this->stringify($how, false);
		$where = $this->stringify($where);

		$query = "UPDATE ".$this->universe."_".$this->table." SET $how WHERE $where";
		return $this->database->query($query);
	}

	public function delete($where) {
		$where = $this->stringify($where);

		$query = "DELETE FROM ".$this->universe."_".$this->table." WHERE $where";
		return $this->database->query($query);
	}

	private function stringify($array, $and = true) {
	  if (!is_array($array)) {
	    if ($array == 1) {
	      return $array;
	    } else {
	      throw new Exception("Data needs to be in array form");
	    }
	  }
	  $line = "";
		if (gettype($array) == "array") {
			$chars_to_delete = 1;
			foreach($array as $key => $value) {
				$value = mysql_real_escape_string($value);
				$key = mysql_real_escape_string($key);

				if ($value == "NOW()")
					$line .= "$key = NOW(),";
				if ($and) {
					$line .= "$key = '$value' AND ";
					$chars_to_delete = 4;
				}
				else
					$line .= "$key = '$value',";
			}
			// delete the last , or AND
			return substr($line, 0, - $chars_to_delete);
		}

		return $array;
	}
}
?>
